home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / DIConfig / cdiacpage.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  50.0 KB  |  1,849 lines

  1. //-----------------------------------------------------------------------------
  2. // File: cdiacpage.cpp
  3. //
  4. // Desc: CDIDeviceActionConfigPage implements the page object used by the UI.
  5. //       A page covers the entire UI minus the device tabs and the bottons at
  6. //       the bottom.  The information window, player combo-box, genre combo-
  7. //       box, action list tree, and device view window are all managed by
  8. //       the page.
  9. //
  10. // Copyright (C) Microsoft Corporation. All Rights Reserved.
  11. //-----------------------------------------------------------------------------
  12.  
  13. #include "common.hpp"
  14. #include <initguid.h>
  15.  
  16. DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
  17.  
  18.  
  19. // {D0B5C9AE-966F-4510-B955-4D2482C5EB1B}
  20. DEFINE_GUID(GUID_ActionItem, 
  21. 0xd0b5c9ae, 0x966f, 0x4510, 0xb9, 0x55, 0x4d, 0x24, 0x82, 0xc5, 0xeb, 0x1b);
  22.  
  23.  
  24. #define DISEM_TYPE_MASK                    ( 0x00000600 )
  25. #define DISEM_REL_MASK                     ( 0x00000100 )
  26. #define DISEM_REL_SHIFT                    ( 8 ) 
  27. #define DISEM_TYPE_AXIS                    0x00000200
  28. #define DISEM_TYPE_BUTTON                  0x00000400
  29. #define DISEM_TYPE_POV                     0x00000600
  30.  
  31. #define DEVICE_POLLING_INTERVAL 10
  32. #define DEVICE_POLLING_AXIS_MIN 0
  33. #define DEVICE_POLLING_AXIS_MAX 100
  34. #define DEVICE_POLLING_AXIS_MINDELTA 3
  35. #define DEVICE_POLLING_AXIS_SIGNIFICANT 40
  36. #define DEVICE_POLLING_AXIS_ACCUMULATION 20
  37. #define DEVICE_POLLING_ACBUF_START_INDEX 3
  38. #define DEVICE_POLLING_WHEEL_SCALE_FACTOR 3
  39.  
  40. // For WINMM.DLL
  41. HINSTANCE g_hWinMmDLL = NULL;
  42. FUNCTYPE_timeSetEvent g_fptimeSetEvent = NULL;
  43.  
  44. //QueryInterface
  45. STDMETHODIMP CDIDeviceActionConfigPage::QueryInterface(REFIID iid, LPVOID* ppv)
  46. {
  47.    //null the out param
  48.     *ppv = NULL;
  49.  
  50.     if ((iid == IID_IUnknown) || (iid == IID_IDIDeviceActionConfigPage))
  51.     {
  52.         *ppv = this;
  53.         AddRef();
  54.         return S_OK;
  55.     }
  56.  
  57.     return E_NOINTERFACE;
  58. }
  59.  
  60.  
  61. //AddRef
  62. STDMETHODIMP_(ULONG) CDIDeviceActionConfigPage::AddRef()
  63. {
  64.     return InterlockedIncrement(&m_cRef);
  65. }
  66.  
  67.  
  68. //Release
  69. STDMETHODIMP_(ULONG) CDIDeviceActionConfigPage::Release()
  70. {
  71.     LONG cRef;
  72.     
  73.     cRef = InterlockedDecrement(&m_cRef);
  74.     if (cRef == 0)
  75.     {
  76.         delete this;
  77.     }
  78.  
  79.     return cRef;
  80. }
  81.  
  82.  
  83. //constructor
  84. CDIDeviceActionConfigPage::CDIDeviceActionConfigPage() :
  85.     m_pDeviceUI(NULL), m_puig(NULL), m_pUIFrame(NULL),
  86.     m_cRef(1), m_lpDiac(NULL), m_lpDID(NULL), m_State(CFGSTATE_NORMAL),
  87.     m_pCurControl(NULL),
  88.     m_tszIBText(NULL), m_pbmIB(NULL), m_pbmIB2(NULL),
  89.     m_pbmRelAxesGlyph(NULL), m_pbmAbsAxesGlyph(NULL), m_pbmButtonGlyph(NULL),
  90.     m_pbmHatGlyph(NULL), m_pbmCheckGlyph(NULL), m_pbmCheckGlyphDark(NULL),
  91.     m_pRelAxesParent(NULL), m_pAbsAxesParent(NULL), m_pButtonParent(NULL),
  92.     m_pHatParent(NULL),    m_pUnknownParent(NULL),
  93.     m_bFirstDeviceData(TRUE), m_cbDeviceDataSize(0), m_nOnDeviceData(0),
  94.     m_dwLastControlType(0),
  95.     m_nPageIndex(-1)
  96. {
  97.     m_pDeviceData[0] = NULL;
  98.     m_pDeviceData[1] = NULL;
  99. }
  100.  
  101.  
  102. //destructor
  103. CDIDeviceActionConfigPage::~CDIDeviceActionConfigPage()
  104. {
  105.     // Unattach the parent from the tooltip window so it won't get destroyed.
  106.     SetParent(CFlexWnd::s_ToolTip.m_hWnd, NULL);
  107.  
  108.     if (m_hWnd != NULL)
  109.         Destroy();
  110.  
  111.     FreeResources();
  112.  
  113.     delete m_pDeviceUI;
  114.  
  115.     for (int c = 0; c < 2; c++)
  116.         if (m_pDeviceData[c] != NULL)
  117.             free(m_pDeviceData[c]);
  118.  
  119.     if (m_lpDID != NULL)
  120.     {
  121.         m_lpDID->Unacquire();
  122.         m_lpDID->Release();
  123.     }
  124.     m_lpDID = NULL;
  125. }
  126.  
  127.  
  128. STDMETHODIMP CDIDeviceActionConfigPage::Create(DICFGPAGECREATESTRUCT *pcs)
  129. {
  130.     if (pcs == NULL)
  131.         return E_INVALIDARG;
  132.     DICFGPAGECREATESTRUCT &cs = *pcs;
  133.     
  134.     // validate/save uig and uif
  135.     m_puig = pcs->pUIGlobals;
  136.     m_pUIFrame = pcs->pUIFrame;
  137.     if (m_puig == NULL || m_pUIFrame == NULL)
  138.         return E_INVALIDARG;
  139.  
  140.     // save page index
  141.     m_nPageIndex = pcs->nPage;
  142.     assert(m_nPageIndex >= 0);
  143.  
  144.     // create deviceui with uig, or fail
  145.     m_pDeviceUI = new CDeviceUI(*m_puig, *m_pUIFrame);
  146.     if (m_pDeviceUI == NULL)
  147.         return E_FAIL;
  148.  
  149.     // save the device instance
  150.     m_didi = cs.didi;
  151.     m_lpDID = cs.lpDID;
  152.     if (m_lpDID != NULL)
  153.         m_lpDID->AddRef();
  154.  
  155.     // create the window
  156.     HWND hWnd = NULL;
  157.     assert(m_puig != NULL);
  158.     RECT rect = {0, 0, 1, 1};
  159.     hWnd = CFlexWnd::Create(cs.hParentWnd, rect, FALSE);
  160.  
  161.     // return the handle
  162.     cs.hPageWnd = hWnd;
  163.  
  164.     assert(m_puig != NULL);
  165.  
  166.     // Create the information box
  167.     m_InfoBox.Create(m_hWnd, g_InfoWndRect, TRUE);
  168.     m_InfoBox.SetFont((HFONT)m_puig->GetFont(UIE_USERNAMES));
  169.     m_InfoBox.SetColors(m_puig->GetTextColor(UIE_USERNAMES),
  170.                         m_puig->GetBkColor(UIE_USERNAMES),
  171.                         m_puig->GetTextColor(UIE_USERNAMESEL),
  172.                         m_puig->GetBkColor(UIE_USERNAMESEL),
  173.                         m_puig->GetBrushColor(UIE_USERNAMES),
  174.                         m_puig->GetPenColor(UIE_USERNAMES));
  175.     SetAppropriateDefaultText();
  176.  
  177.     // Create the check box only if this is a keyboard device.
  178.     if (LOBYTE(LOWORD(m_didi.dwDevType)) == DI8DEVTYPE_KEYBOARD)
  179.     {
  180.         m_CheckBox.Create(m_hWnd, g_CheckBoxRect, FALSE);
  181.         m_CheckBox.SetNotify(m_hWnd);
  182.         m_CheckBox.SetFont((HFONT)m_puig->GetFont(UIE_USERNAMES));
  183.         m_CheckBox.SetColors(m_puig->GetTextColor(UIE_USERNAMES),
  184.                              m_puig->GetBkColor(UIE_USERNAMES),
  185.                              m_puig->GetTextColor(UIE_USERNAMESEL),
  186.                              m_puig->GetBkColor(UIE_USERNAMESEL),
  187.                              m_puig->GetBrushColor(UIE_USERNAMES),
  188.                              m_puig->GetPenColor(UIE_USERNAMES));
  189.  
  190.         TCHAR tszResourceString[MAX_PATH];
  191.         LoadString(g_hModule, IDS_SORTASSIGNED, tszResourceString, MAX_PATH);
  192.         m_CheckBox.SetText(tszResourceString);
  193.         m_CheckBox.SetCheck(TRUE);
  194.         ::ShowWindow(m_CheckBox.m_hWnd, SW_SHOW);
  195.     }
  196.  
  197.     // create the username dropdown if necessary
  198.     FLEXCOMBOBOXCREATESTRUCT cbcs;
  199.     cbcs.dwSize = sizeof(FLEXCOMBOBOXCREATESTRUCT);
  200.     cbcs.dwFlags = FCBF_DEFAULT;
  201.     cbcs.dwListBoxFlags = FCBF_DEFAULT|FLBF_INTEGRALHEIGHT;
  202.     cbcs.hWndParent = m_hWnd;
  203.     cbcs.hWndNotify = m_hWnd;
  204.     cbcs.bVisible = TRUE;
  205.     cbcs.rect = g_UserNamesRect;
  206.     cbcs.hFont = (HFONT)m_puig->GetFont(UIE_USERNAMES);
  207.     cbcs.rgbText = m_puig->GetTextColor(UIE_USERNAMES);
  208.     cbcs.rgbBk = m_puig->GetBkColor(UIE_USERNAMES);
  209.     cbcs.rgbSelText = m_puig->GetTextColor(UIE_USERNAMESEL);
  210.     cbcs.rgbSelBk = m_puig->GetBkColor(UIE_USERNAMESEL);
  211.     cbcs.rgbFill = m_puig->GetBrushColor(UIE_USERNAMES);
  212.     cbcs.rgbLine = m_puig->GetPenColor(UIE_USERNAMES);
  213.     cbcs.nSBWidth = 11;
  214.  
  215.     if (m_puig->GetNumUserNames() > 0 && m_hWnd != NULL
  216.        )
  217.     {
  218.         for (int i = 0, n = m_puig->GetNumUserNames(); i < n; i++)
  219.             m_UserNames.AddString(SAFESTR(m_puig->GetUserName(i)));
  220.         m_UserNames.AddString(SAFESTR(_T("(unassigned)")));
  221.  
  222.         m_UserNames.Create(&cbcs);
  223.  
  224.         int nUser = m_pUIFrame->GetCurUser(m_nPageIndex);
  225.         if (nUser == -1)
  226.             nUser = m_puig->GetNumUserNames();
  227.         m_UserNames.SetSel(nUser);
  228.     } else
  229.     if (m_hWnd != NULL)
  230.         m_UserNames.SetSel(0);  // If only 1 user, still must set selection to 0 or we get error later.
  231.  
  232.     // If we are in view mode, set username combobox to read only so user can't change its value.
  233.     if (!m_puig->InEditMode())
  234.         m_UserNames.SetReadOnly(TRUE);
  235.  
  236.     if (m_puig->GetNumMasterAcFors() > 1 && m_hWnd != NULL)
  237.     {
  238.         for (int i = 0, n = m_puig->GetNumMasterAcFors(); i < n; i++)
  239.             m_Genres.AddString(SAFESTR(m_puig->RefMasterAcFor(i).tszActionMap));
  240.  
  241.         cbcs.rect = g_GenresRect;
  242.         m_Genres.Create(&cbcs);
  243.         m_Genres.SetSel(m_pUIFrame->GetCurGenre());
  244.     }
  245.  
  246.     // return success/fail
  247.     return hWnd != NULL ? S_OK : E_FAIL;
  248. }
  249.  
  250. STDMETHODIMP CDIDeviceActionConfigPage::Show(LPDIACTIONFORMATW lpDiActFor)
  251. {
  252.     // save the format pointer
  253.     m_lpDiac = lpDiActFor;
  254.  
  255.     // force tree init
  256.     InitTree(TRUE);
  257.  
  258.     // show the assignments for the controls
  259.     SetControlAssignments();
  260.  
  261.     // show the assignment for the current control
  262.     ShowCurrentControlAssignment();
  263.  
  264.     // Sort the list if check box is checked.
  265.     if (m_CheckBox.GetCheck())
  266.         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  267.  
  268.     // show the window
  269.     if (m_hWnd != NULL)
  270.         ShowWindow(m_hWnd, SW_SHOW);
  271.  
  272.     SetFocus(m_hWnd);
  273.     CFlexWnd::s_CurrPageHwnd = m_hWnd;
  274.  
  275.     return S_OK;
  276. }
  277.  
  278. STDMETHODIMP CDIDeviceActionConfigPage::Hide()
  279. {
  280.     // clear the tree
  281.     ClearTree();
  282.  
  283.     // null the format pointer
  284.     m_lpDiac = NULL;
  285.  
  286.     // hide the window
  287.     if (m_hWnd != NULL)
  288.         ShowWindow(m_hWnd, SW_HIDE);
  289.  
  290.     // If we are in the assign state, exit it.
  291.     if (m_State == CFGSTATE_ASSIGN)
  292.         ExitAssignState();
  293.  
  294.     return S_OK;
  295. }
  296.  
  297. void CDIDeviceActionConfigPage::InitIB()
  298. {
  299.     RECT z = {0,0,0,0};
  300.     SIZE bsize = {0,0};
  301.     m_rectIB = z;
  302.     if (m_pbmIB != NULL)
  303.     {
  304.         if (m_pbmIB->GetSize(&bsize))
  305.         {
  306.             m_rectIB.right = bsize.cx * 2;
  307.             m_rectIB.bottom = bsize.cy;
  308.         }
  309.     }
  310.  
  311.     const int IBORIGINX = 200, IBORIGINY = 394,
  312.         IBTEXTMARGINLEFT = 5;
  313.     POINT ptIBOrigin = {IBORIGINX, IBORIGINY};
  314.  
  315.     m_tszIBText = _T("Click here to see different views of your controller.");
  316.     SIZE tsize = GetTextSize(m_tszIBText, (HFONT)m_puig->GetFont(UIE_VIEWSEL));
  317.     m_ptIBOffset.x = 0;
  318.     m_ptIBOffset.y = 0;
  319.     int tofs = 0;
  320.     if (m_rectIB.bottom < tsize.cy)
  321.     {
  322.         m_rectIB.bottom = tsize.cy;
  323.         m_ptIBOffset.y = (tsize.cy - bsize.cy) / 2;
  324.     }
  325.     else if (tsize.cy < m_rectIB.bottom)
  326.         tofs = (bsize.cy - tsize.cy) / 2;
  327.     m_rectIB.right += tsize.cx;
  328.     if (m_pbmIB != NULL)
  329.         m_rectIB.right += IBTEXTMARGINLEFT * 2;
  330.  
  331.     OffsetRect(&m_rectIB, ptIBOrigin.x, ptIBOrigin.y);
  332.     m_ptIBOffset.x += ptIBOrigin.x;
  333.     m_ptIBOffset.y += ptIBOrigin.y;
  334.  
  335.     m_ptIBOffset2.x = m_rectIB.right - bsize.cx;
  336.     m_ptIBOffset2.y = m_ptIBOffset.y;
  337.  
  338.     m_rectIBText = m_rectIB;
  339.     if (m_pbmIB != NULL)
  340.         m_rectIBText.left += IBTEXTMARGINLEFT + bsize.cx;
  341.     if (m_pbmIB2 != NULL)
  342.         m_rectIBText.right -= IBTEXTMARGINLEFT + bsize.cx;
  343.     m_rectIBText.top += tofs;
  344.  
  345.     // Inialize the two RECTs representing the two arrow bitmaps
  346.     m_rectIBLeft = m_rectIBRight = m_rectIB;
  347.     m_rectIBLeft.right = m_rectIBText.left;
  348.     m_rectIBRight.left = m_rectIBText.right;
  349. }
  350.  
  351. void CDIDeviceActionConfigPage::OnInit()
  352. {
  353.     // init resources
  354.     InitResources();
  355.  
  356.     // init IB
  357.     InitIB();
  358.  
  359.     // initialize the device UI
  360.     m_pDeviceUI->Init(m_didi, m_lpDID, m_hWnd, this);
  361.  
  362.     // initialize the device
  363.     InitDevice();
  364.  
  365.     // Start a one-shot timer for click to pick
  366.     if (g_fptimeSetEvent)
  367.         g_fptimeSetEvent(DEVICE_POLLING_INTERVAL, DEVICE_POLLING_INTERVAL,
  368.                          CDIDeviceActionConfigPage::DeviceTimerProc, (DWORD_PTR)m_hWnd, TIME_ONESHOT);
  369.  
  370.     // create the tree
  371.     CAPTIONLOOK cl;
  372.     cl.dwMask = CLMF_TEXTCOLOR | CLMF_FONT | CLMF_LINECOLOR;
  373.     cl.rgbTextColor = m_puig->GetTextColor(UIE_CALLOUT);
  374.     cl.rgbLineColor = m_puig->GetPenColor(UIE_BORDER);
  375.     cl.hFont = (HFONT)m_puig->GetFont(UIE_ACTION);
  376.     m_Tree.SetDefCaptionLook(cl);
  377.     cl.rgbTextColor = m_puig->GetTextColor(UIE_CALLOUTHIGH);
  378.     m_Tree.SetDefCaptionLook(cl, TRUE);
  379.     m_Tree.SetBkColor(RGB(0,0,0));
  380.     if (m_puig->InEditMode())
  381.     {
  382.         m_Tree.Create(m_hWnd, g_TreeRect, TRUE, TRUE);
  383.         m_Tree.SetScrollBarColors(
  384.             m_puig->GetBrushColor(UIE_SBTRACK),
  385.             m_puig->GetBrushColor(UIE_SBTHUMB),
  386.             m_puig->GetPenColor(UIE_SBBUTTON));
  387.     }
  388. }
  389.  
  390. void CDIDeviceActionConfigPage::InitResources()
  391. {
  392.     // create glyphs
  393.     if (!m_pbmRelAxesGlyph)
  394.         m_pbmRelAxesGlyph = CBitmap::CreateFromResource(g_hModule, IDB_AXESGLYPH);
  395.     if (!m_pbmAbsAxesGlyph)
  396.         m_pbmAbsAxesGlyph = CBitmap::CreateFromResource(g_hModule, IDB_AXESGLYPH);
  397.     if (!m_pbmButtonGlyph)
  398.         m_pbmButtonGlyph = CBitmap::CreateFromResource(g_hModule, IDB_BUTTONGLYPH);
  399.     if (!m_pbmHatGlyph)
  400.         m_pbmHatGlyph = CBitmap::CreateFromResource(g_hModule, IDB_HATGLYPH);
  401.     if (!m_pbmCheckGlyph)
  402.         m_pbmCheckGlyph = CBitmap::CreateFromResource(g_hModule, IDB_CHECKGLYPH);
  403.     if (!m_pbmCheckGlyphDark)
  404.         m_pbmCheckGlyphDark = CBitmap::CreateFromResource(g_hModule, IDB_CHECKGLYPHDARK);
  405.  
  406.     // create IB bitmaps
  407.     if (!m_pbmIB)
  408.         m_pbmIB = CBitmap::CreateFromResource(g_hModule, IDB_IB);
  409.     if (!m_pbmIB2)
  410.         m_pbmIB2 = CBitmap::CreateFromResource(g_hModule, IDB_IB2);
  411. }
  412.  
  413. void CDIDeviceActionConfigPage::FreeResources()
  414. {
  415.     if (m_pbmRelAxesGlyph)
  416.         delete m_pbmRelAxesGlyph;
  417.     if (m_pbmAbsAxesGlyph)
  418.         delete m_pbmAbsAxesGlyph;
  419.     if (m_pbmButtonGlyph)
  420.         delete m_pbmButtonGlyph;
  421.     if (m_pbmHatGlyph)
  422.         delete m_pbmHatGlyph;
  423.     if (m_pbmCheckGlyph)
  424.         delete m_pbmCheckGlyph;
  425.     if (m_pbmCheckGlyphDark)
  426.         delete m_pbmCheckGlyphDark;
  427.     if (m_pbmIB)
  428.         delete m_pbmIB;
  429.     if (m_pbmIB2)
  430.         delete m_pbmIB2;
  431.     m_pbmRelAxesGlyph = NULL;
  432.     m_pbmAbsAxesGlyph = NULL;
  433.     m_pbmButtonGlyph = NULL;
  434.     m_pbmHatGlyph = NULL;
  435.     m_pbmCheckGlyph = NULL;
  436.     m_pbmCheckGlyphDark = NULL;
  437.     m_pbmIB = NULL;
  438.     m_pbmIB2 = NULL;
  439. }
  440.  
  441. void CDIDeviceActionConfigPage::ClearTree()
  442. {
  443.     m_Tree.FreeAll();
  444.     m_pRelAxesParent = NULL;
  445.     m_pAbsAxesParent = NULL;
  446.     m_pButtonParent = NULL;
  447.     m_pHatParent = NULL;
  448.     m_pUnknownParent = NULL;
  449.     m_dwLastControlType = 0;
  450. }
  451.  
  452. void CDIDeviceActionConfigPage::InitTree(BOOL bForceInit)
  453. {
  454.     // get type of control
  455.     DWORD dwControlType = 0;
  456.     if (m_pCurControl && m_pCurControl->IsOffsetAssigned())
  457.     {
  458.         DWORD dwObjId = m_pCurControl->GetOffset();
  459.  
  460.         if (dwObjId & DIDFT_RELAXIS)
  461.             dwControlType = DIDFT_RELAXIS;
  462.         else if (dwObjId & DIDFT_ABSAXIS)
  463.             dwControlType = DIDFT_ABSAXIS;
  464.         else if (dwObjId & DIDFT_BUTTON)
  465.             dwControlType = DIDFT_BUTTON;
  466.         else if (dwObjId & DIDFT_POV)
  467.             dwControlType = DIDFT_POV;
  468.     }
  469.  
  470.     // Turn off the tree's readonly flag if we are in the assign state.
  471.     // We will turn it on later if current control's action has DIA_APPFIXED.
  472.     if (m_State == CFGSTATE_NORMAL)
  473.         m_Tree.SetReadOnly(TRUE);
  474.     else
  475.         m_Tree.SetReadOnly(FALSE);
  476.  
  477.     // if this control type is the same as the last, do nothing,
  478.     // unless we're force init
  479.     if (m_dwLastControlType == dwControlType && !bForceInit && m_State)
  480.         return;
  481.  
  482.     // delete the whole tree
  483.     ClearTree();
  484.  
  485.     // can't use tree if there is no diac or action array
  486.     if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL)
  487.         return;
  488.  
  489.     // also can't use if we don't have a control type
  490.     if (dwControlType == 0)
  491.         return;
  492.  
  493.     // prepare margin rects
  494.     RECT labelmargin = {14, 6, 3, 3};
  495.     RECT itemmargin = {14, 1, 3, 2};
  496.  
  497.     // set default indents
  498.     m_Tree.SetRootChildIndent(5);
  499.     m_Tree.SetDefChildIndent(12);
  500.  
  501.     // add the control type sections
  502.     m_Tree.SetDefMargin(labelmargin);
  503.     TCHAR tszResourceString[MAX_PATH];
  504.     switch (dwControlType)
  505.     {
  506.         case DIDFT_RELAXIS:
  507.             LoadString(g_hModule, IDS_AXISACTIONS, tszResourceString, MAX_PATH);
  508.             m_pRelAxesParent = m_Tree.DefAddItem(tszResourceString);
  509.             break;
  510.  
  511.         case DIDFT_ABSAXIS:
  512.             LoadString(g_hModule, IDS_AXISACTIONS, tszResourceString, MAX_PATH);
  513.             m_pAbsAxesParent = m_Tree.DefAddItem(tszResourceString);
  514.             break;
  515.  
  516.         case DIDFT_BUTTON:
  517.             LoadString(g_hModule, IDS_BUTTONACTIONS, tszResourceString, MAX_PATH);
  518.             m_pButtonParent = m_Tree.DefAddItem(tszResourceString);
  519.             break;
  520.  
  521.         case DIDFT_POV:
  522.             LoadString(g_hModule, IDS_POVACTIONS, tszResourceString, MAX_PATH);
  523.             m_pHatParent = m_Tree.DefAddItem(tszResourceString);
  524.             break;
  525.  
  526.         default:
  527.             return;
  528.     }
  529.  
  530.     // populate the tree
  531.     m_Tree.SetDefMargin(itemmargin);
  532.     for (unsigned int i = 0; i < m_lpDiac->dwNumActions; i++)
  533.     {
  534.         DIACTIONW *pAction = m_lpDiac->rgoAction + i;
  535.         CFTItem *pItem = NULL;
  536.  
  537.         if (pAction == NULL) 
  538.             continue;
  539.  
  540.         switch (pAction->dwSemantic & DISEM_TYPE_MASK)
  541.         {
  542.             case DISEM_TYPE_AXIS:
  543.                 // Must distinguish between relative and absolute
  544.                 switch((pAction->dwSemantic & DISEM_REL_MASK) >> DISEM_REL_SHIFT)
  545.                 {
  546.                     case 0: pItem = m_pAbsAxesParent; break;
  547.                     case 1: pItem = m_pRelAxesParent; break;
  548.                 }
  549.                 break;
  550.             case DISEM_TYPE_BUTTON: pItem = m_pButtonParent; break;
  551.             case DISEM_TYPE_POV: pItem = m_pHatParent; break;
  552.         }
  553.  
  554.         if (pItem == NULL)
  555.             continue;
  556.  
  557.         // Add action with this name
  558.         CFTItem *pAlready = GetItemWithActionNameAndSemType(pAction->lptszActionName, pAction->dwSemantic);
  559.         if (!pAlready)
  560.         {
  561.             LPTSTR acname = AllocLPTSTR(pAction->lptszActionName);
  562.             pItem = m_Tree.DefAddItem(acname, pItem, ATTACH_LASTCHILD);  // This might return NULL.
  563.             free(acname);
  564.             if (pItem)
  565.                 pItem->SetUserData((LPVOID)(new RGLPDIACW));
  566.         }
  567.         else
  568.         {
  569.             pItem = pAlready;
  570.         }
  571.  
  572.         if (pItem == NULL)
  573.             continue;
  574.  
  575.         pItem->SetUserGUID(GUID_ActionItem);
  576.         RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  577.         if (pacs)
  578.             pacs->SetAtGrow(pacs->GetSize(), pAction);
  579.  
  580.         if (pAlready)
  581.         {
  582.             // The tree already has an action with this name.  Check the DIA_APPFIXED flag for each DIACTION
  583.             // that this item holds.
  584.             DWORD dwNumActions = GetNumItemLpacs(pItem);
  585.             for (DWORD i = 0; i < dwNumActions; ++i)
  586.             {
  587.                 LPDIACTIONW lpExistingAc = GetItemLpac(pItem, i);
  588.                 // If the DIACTION that is assigned to this device has DIA_APPFIXED flag, then
  589.                 //   the other must have it too.
  590.                 if (lpExistingAc && IsEqualGUID(lpExistingAc->guidInstance, m_didi.guidInstance))
  591.                 {
  592.                     if (lpExistingAc->dwFlags & DIA_APPFIXED)
  593.                     {
  594.                         // If this DIACTION has DIA_APPFIXED, then all DIACTIONs must have it too.
  595.                         for (DWORD j = 0; j < dwNumActions; ++j)
  596.                         {
  597.                             LPDIACTIONW lpChangeAc = GetItemLpac(pItem, j);
  598.                             if (lpChangeAc)
  599.                                 lpChangeAc->dwFlags |= DIA_APPFIXED;
  600.                         }
  601.                     }
  602.  
  603.                     break;  // Break the loop since we already found the DIACTION that is assigned.
  604.                 }
  605.             }
  606.         }  // if (pAlready)
  607.     }
  608.  
  609.     // show all
  610.     m_Tree.GetRoot()->ExpandAll();
  611.     m_dwLastControlType = dwControlType;
  612. }
  613.  
  614. int CompareActionNames(LPCWSTR acname1, LPCWSTR acname2)
  615. {
  616. #ifdef CFGUI__COMPAREACTIONNAMES_CASE_INSENSITIVE
  617.     return _wcsicmp(acname1, acname2);
  618. #else
  619.     return wcscmp(acname1, acname2);
  620. #endif
  621. }
  622.  
  623. CFTItem *CDIDeviceActionConfigPage::GetItemWithActionNameAndSemType(LPCWSTR acname, DWORD dwSemantic)
  624. {
  625.     CFTItem *pItem = m_Tree.GetFirstItem();
  626.     for (; pItem != NULL; pItem = pItem->GetNext())
  627.     {
  628.         if (!pItem->IsUserGUID(GUID_ActionItem))
  629.             continue;
  630.         
  631.         LPDIACTIONW lpac = GetItemLpac(pItem);
  632.         if (!lpac)
  633.             continue;
  634.  
  635.         // Check semantic type
  636.         if ((lpac->dwSemantic & DISEM_TYPE_MASK) != (dwSemantic & DISEM_TYPE_MASK))
  637.             continue;
  638.  
  639.         // If both are axis, check for relative/absolute
  640.         if ((lpac->dwSemantic & DISEM_TYPE_MASK) == DISEM_TYPE_AXIS)
  641.             if ((lpac->dwSemantic & DISEM_REL_MASK) != (dwSemantic & DISEM_REL_MASK))
  642.                 continue;
  643.  
  644.         // Check name
  645.         if (CompareActionNames(lpac->lptszActionName, acname) == 0)
  646.             return pItem;
  647.     }
  648.  
  649.     return NULL;
  650. }
  651.  
  652. void CDIDeviceActionConfigPage::OnPaint(HDC hDC)
  653. {
  654.     TCHAR tszResourceString[MAX_PATH];
  655.     CPaintHelper ph(*m_puig, hDC);
  656.  
  657.     ph.SetBrush(UIB_BLACK);
  658.     RECT rect;
  659.     GetClientRect(&rect);
  660.     ph.Rectangle(rect, UIR_SOLID);
  661.  
  662.     ph.SetText(UIC_BORDER, UIC_BLACK);
  663.     {
  664.         rect = g_UserNamesTitleRect;
  665.         LoadString(g_hModule, IDS_PLAYER_TITLE, tszResourceString, MAX_PATH);
  666.         DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
  667.     }
  668.     if (m_puig->GetNumMasterAcFors() > 1)
  669.     {
  670.         rect = g_GenresTitleRect;
  671.         LoadString(g_hModule, IDS_GENRE_TITLE, tszResourceString, MAX_PATH);
  672.         DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
  673.     }
  674.  
  675.     // Draw tree window title and outline if we are in edit mode.
  676.     if (m_puig->InEditMode())
  677.     {
  678.         COLORREF BorderColor = m_puig->GetColor(UIC_BORDER);
  679.         if (m_Tree.GetReadOnly())
  680.             BorderColor = RGB(GetRValue(BorderColor)>>1, GetGValue(BorderColor)>>1, GetBValue(BorderColor)>>1);
  681.  
  682.         ::SetTextColor(hDC, BorderColor);  // Use the muted color if tree is read only.
  683.         // Draw tree window title (Available Actions)
  684.         rect = g_TreeTitleRect;
  685.         LoadString(g_hModule, IDS_AVAILABLEACTIONS_TITLE, tszResourceString, MAX_PATH);
  686.         DrawText(hDC, tszResourceString, -1, &rect, DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
  687.         // Draw tree window outline
  688.         HGDIOBJ hPen, hOldPen;
  689.         if (m_Tree.GetReadOnly())
  690.         {
  691.             hPen = CreatePen(PS_SOLID, 0, BorderColor);
  692.             hOldPen = ::SelectObject(hDC, hPen);
  693.         }
  694.         else
  695.             ph.SetPen(UIP_BORDER);
  696.  
  697.         RECT rc = g_TreeRect;
  698.         InflateRect(&rc, 1, 1);
  699.         Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
  700.         if (m_Tree.GetReadOnly())
  701.         {
  702.             ::SelectObject(hDC, hOldPen);
  703.             DeleteObject(hPen);
  704.         }
  705.     }
  706.  
  707.     if (m_pDeviceUI->GetNumViews() < 2)
  708.         return;
  709.  
  710.     if (m_pbmIB != NULL)
  711.         m_pbmIB->Draw(hDC, m_ptIBOffset);
  712.     if (m_pbmIB2 != NULL)
  713.         m_pbmIB2->Draw(hDC, m_ptIBOffset2);
  714.     if (m_tszIBText != NULL)
  715.     {
  716.         ph.SetElement(UIE_VIEWSEL);
  717.         RECT rect = m_rectIBText;
  718.         DrawText(hDC, m_tszIBText, -1, &rect, DT_NOCLIP | DT_NOPREFIX);
  719.     }
  720. }
  721.  
  722. void CDIDeviceActionConfigPage::SetCurrentControl(CDeviceControl *pControl)
  723. {
  724.     // If the new control is the same as the old, no need to do anything.
  725.     if (m_pCurControl == pControl)
  726.         return;
  727.     if (m_pCurControl != NULL)
  728.     {
  729.         m_pCurControl->Unhighlight();
  730.         // If we don't have a current control, then invalidate the view so that the old callout can be repainted.
  731.         // If there is a current control, the view will be invalidated by Highlight().
  732.         if (!pControl)
  733.             m_pCurControl->Invalidate();
  734.     }
  735.     m_pCurControl = pControl;
  736.     if (m_pCurControl != NULL)
  737.         m_pCurControl->Highlight();
  738.     ShowCurrentControlAssignment();
  739. }
  740.  
  741. CFTItem *CDIDeviceActionConfigPage::GetItemForActionAssignedToControl(CDeviceControl *pControl)
  742. {
  743.     if (!pControl)
  744.         return NULL;
  745.  
  746.     // find the item for the action assigned to this control, if any
  747.     CFTItem *pItem = m_Tree.GetFirstItem();
  748.     for (; pItem != NULL; pItem = pItem->GetNext())
  749.     {
  750.         if (!pItem->IsUserGUID(GUID_ActionItem))
  751.             continue;
  752.  
  753.         for (int i = 0, n = GetNumItemLpacs(pItem); i < n; i++)
  754.         {
  755.             LPDIACTIONW lpac = GetItemLpac(pItem, i);
  756.             if (!lpac)
  757.                 continue;
  758.  
  759.             if (IsEqualGUID(lpac->guidInstance, m_didi.guidInstance) &&
  760.                     GetOffset(lpac) == pControl->GetOffset())
  761.                 return pItem;
  762.         }
  763.     }
  764.  
  765.     return NULL;
  766. }
  767.  
  768. int CDIDeviceActionConfigPage::GetNumItemLpacs(CFTItem *pItem)
  769. {
  770.     if (pItem == NULL)
  771.         return 0;
  772.  
  773.     RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  774.     if (!pacs)
  775.         return 0;
  776.     else
  777.         return pacs->GetSize();
  778. }
  779.  
  780. LPDIACTIONW CDIDeviceActionConfigPage::GetItemLpac(CFTItem *pItem, int i)
  781. {
  782.     if (pItem == NULL)
  783.         return NULL;
  784.  
  785.     RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  786.     if (!pacs || i < 0 || i >= pacs->GetSize())
  787.         return NULL;
  788.     else    
  789.         return pacs->GetAt(i);
  790. }
  791.  
  792. void CDIDeviceActionConfigPage::ShowCurrentControlAssignment()
  793. {
  794.     // init the tree
  795.     InitTree();
  796.  
  797.     // if we don't have a control...
  798.     if (m_pCurControl == NULL)
  799.     {
  800.         // select nothing
  801.         m_Tree.SetCurSel(NULL);
  802.         return;
  803.     }
  804.  
  805.     // find the item for the action assigned to this control, if any
  806.     CFTItem *pItem = GetItemForActionAssignedToControl(m_pCurControl);
  807.  
  808.     // if we didn't find a match...
  809.     if (!pItem)
  810.     {
  811.         // select nothing
  812.         m_Tree.SetCurSel(NULL);
  813.         return;
  814.     }
  815.  
  816.     // We need to check if the action this control is assigned to has DIA_APPFIXED flag.
  817.     // If it does, this control cannot be remapped to another action.
  818.     // We prevent this by setting the tree control to read-only, so it can't receive any clicks.
  819.     LPDIACTIONW lpAc = GetItemLpac(pItem);  // Get the action
  820.     if (lpAc && (lpAc->dwFlags & DIA_APPFIXED))
  821.         m_Tree.SetReadOnly(TRUE);
  822.  
  823.     // otherwise, show item and select it
  824.     pItem->EnsureVisible();
  825.     m_Tree.SetCurSel(pItem);
  826. }
  827.  
  828. void CDIDeviceActionConfigPage::DeviceUINotify(const DEVICEUINOTIFY &uin)
  829. {
  830.     switch (uin.msg)
  831.     {
  832.         case DEVUINM_NUMVIEWSCHANGED:
  833.             Invalidate();
  834.             break;
  835.  
  836.         case DEVUINM_SELVIEW:
  837.             // set the view
  838.             m_pDeviceUI->SetView(uin.selview.nView);
  839.  
  840.             // show the assignments for the controls
  841.             SetControlAssignments();
  842.  
  843.             // select nothing
  844.             SetCurrentControl(NULL);
  845.             break;
  846.  
  847.         case DEVUINM_ONCONTROLDESTROY:
  848.             if (uin.control.pControl == m_pCurControl)
  849.                 m_pCurControl = NULL;
  850.             break;
  851.  
  852.         case DEVUINM_CLICK:
  853.             ExitAssignState();
  854.             switch (uin.from)
  855.             {
  856.                 case DEVUINFROM_CONTROL:
  857.                     SetCurrentControl(uin.control.pControl);
  858.                     SetAppropriateDefaultText();
  859.                     break;
  860.                 case DEVUINFROM_VIEWWND:
  861.                     break;
  862.             }
  863.             break;
  864.  
  865.         case DEVUINM_DOUBLECLICK:
  866.             switch (uin.from)
  867.             {
  868.                 case DEVUINFROM_CONTROL:
  869.                     EnterAssignState();
  870.                     break;
  871.             }
  872.             break;
  873.  
  874.         case DEVUINM_MOUSEOVER:
  875.             SetAppropriateDefaultText();
  876.             break;
  877.  
  878.         case DEVUINM_RENEWDEVICE:
  879.             HWND hParent = GetParent(m_hWnd);
  880.             CConfigWnd *pCfgWnd = (CConfigWnd *)GetFlexWnd(hParent);
  881.             if (pCfgWnd)
  882.             {
  883.                 LPDIRECTINPUTDEVICE8W lpDID = pCfgWnd->RenewDevice(m_didi.guidInstance);
  884.                 if (lpDID)
  885.                 {
  886.                     // Destroy the device instance we have
  887.                     if (m_lpDID) m_lpDID->Release();
  888.                     lpDID->AddRef();
  889.                     m_lpDID = lpDID;
  890.                 }
  891.                 m_pDeviceUI->SetDevice(lpDID);  // Sets the device pointer in CDeviceUI (no need to AddRef)
  892.             }
  893.     }
  894. }
  895.  
  896. void CDIDeviceActionConfigPage::UnassignCallout()
  897. {
  898.     // find the item for the action assigned to this control, if any
  899.     CFTItem *pItem = GetItemForActionAssignedToControl(m_pCurControl);
  900.     if (pItem)
  901.     {
  902.         LPDIACTIONW lpac = GetItemLpac(pItem);
  903.         // Only unassign if the action doesn't have DIA_APPFIXED flag.
  904.         if (lpac && !(lpac->dwFlags & DIA_APPFIXED))
  905.         {
  906.             ActionClick(NULL);
  907.             m_Tree.Invalidate();
  908.         }
  909.     }
  910.     // Sort the list if the check box is checked.
  911.     if (m_CheckBox.GetCheck())
  912.         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  913. }
  914.  
  915. void CDIDeviceActionConfigPage::NullAction(LPDIACTIONW lpac)
  916. {
  917.     if (lpac == NULL)
  918.         return;
  919.  
  920.     SetInvalid(lpac);
  921.  
  922. }
  923.  
  924. void CDIDeviceActionConfigPage::UnassignActionsAssignedTo(const GUID &guidInstance, DWORD dwOffset)
  925. {
  926.     if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL)
  927.         return;
  928.  
  929.     if (IsEqualGUID(guidInstance, GUID_NULL))
  930.         return;
  931.  
  932.     // assign any actions assigned to this control to nothing
  933.     DWORD i;
  934.     LPDIACTIONW lpac;
  935.     for (i = 0, lpac = m_lpDiac->rgoAction; i < m_lpDiac->dwNumActions; i++, lpac++)
  936.         if (IsEqualGUID(guidInstance, lpac->guidInstance) && dwOffset == GetOffset(lpac)/*->dwInternalOffset*/)
  937.         {
  938.             GlobalUnassignControlAt(guidInstance, dwOffset);
  939.             NullAction(lpac);
  940.         }
  941. }
  942.  
  943. void CDIDeviceActionConfigPage::UnassignControl(CDeviceControl *pControl)
  944. {
  945.     if (pControl == NULL)
  946.         return;
  947.  
  948.     // make sure the control itself indicates unassignment
  949.     pControl->SetCaption(g_tszUnassignedControlCaption);
  950. }
  951.  
  952. void CallUnassignControl(CDeviceControl *pControl, LPVOID pVoid, BOOL bFixed)
  953. {
  954.     CDIDeviceActionConfigPage *pThis = (CDIDeviceActionConfigPage *)pVoid;
  955.     pThis->UnassignControl(pControl);
  956. }
  957.  
  958. void CDIDeviceActionConfigPage::GlobalUnassignControlAt(const GUID &guidInstance, DWORD dwOffset)
  959. {
  960.     if (IsEqualGUID(guidInstance, GUID_NULL))
  961.         return;
  962.  
  963.     if (IsEqualGUID(guidInstance, m_didi.guidInstance))
  964.         m_pDeviceUI->DoForAllControlsAtOffset(dwOffset, CallUnassignControl, this);
  965. }
  966.  
  967. // this function must find whatever control is assigned to this action and unassign it
  968. void CDIDeviceActionConfigPage::UnassignAction(LPDIACTIONW slpac)
  969. {
  970.     // call UnassignSpecificAction for each action with the same name
  971.     // as this one, including this one
  972.     
  973.     if (slpac == NULL)
  974.         return;
  975.  
  976.     CFTItem *pItem = GetItemWithActionNameAndSemType(slpac->lptszActionName, slpac->dwSemantic);
  977.     if (!pItem)
  978.         return;
  979.  
  980.     RGLPDIACW *pacs = (RGLPDIACW *)pItem->GetUserData();
  981.     if (!pacs)
  982.         return;
  983.  
  984.     for (int i = 0; i < pacs->GetSize(); i++)
  985.         UnassignSpecificAction(pacs->GetAt(i));
  986. }
  987.  
  988. void CDIDeviceActionConfigPage::UnassignSpecificAction(LPDIACTIONW lpac)
  989. {
  990.     if (lpac == NULL)
  991.         return;
  992.  
  993.     if (IsEqualGUID(lpac->guidInstance, GUID_NULL))
  994.         return;
  995.  
  996.     // if there's a control with this instance/offset, unassign it
  997.     UnassignActionsAssignedTo(lpac->guidInstance, GetOffset(lpac)/*->dwInternalOffset*/);
  998.     GlobalUnassignControlAt(lpac->guidInstance, GetOffset(lpac)/*->dwInternalOffset*/);
  999.  
  1000.     // now actually null the action
  1001.     NullAction(lpac);
  1002. }
  1003.  
  1004. void CDIDeviceActionConfigPage::AssignCurrentControlToAction(LPDIACTIONW lpac)
  1005. {
  1006.     // if there is a control, unassign it
  1007.     if (m_pCurControl != NULL)
  1008.     {
  1009.         UnassignControl(m_pCurControl);
  1010.         GUID guidInstance;
  1011.         DWORD dwOffset;
  1012.         m_pCurControl->GetInfo(guidInstance, dwOffset);
  1013.         UnassignActionsAssignedTo(guidInstance, dwOffset);
  1014.     }
  1015.  
  1016.     // if there is an action, unassign it
  1017.     if (lpac != NULL)
  1018.         UnassignAction(lpac);
  1019.  
  1020.     // can only continue if we have both
  1021.     if (lpac == NULL || m_pCurControl == NULL)
  1022.         return;
  1023.  
  1024.     // here we should have a control and an action
  1025.     assert(lpac != NULL);
  1026.     assert(m_pCurControl != NULL);
  1027.  
  1028.     // because an action can only be assigned to one control,
  1029.     // make sure this action is unassigned first
  1030.     UnassignAction(lpac);
  1031.  
  1032.     // now actually assign
  1033.     DWORD ofs;
  1034.     m_pCurControl->GetInfo(lpac->guidInstance, ofs/*lpac->dwInternalOffset*/);
  1035.     SetOffset(lpac, ofs);
  1036.     LPTSTR acname = AllocLPTSTR(lpac->lptszActionName);
  1037.     m_pCurControl->SetCaption(acname, lpac->dwFlags & DIA_APPFIXED);
  1038.     free(acname);
  1039.  
  1040.     // Sort the action list if check box is checked
  1041.     if (m_CheckBox.GetCheck())
  1042.     {
  1043.         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  1044.         // Scroll so that we scroll to make this visible since it might be displaced by sorting.
  1045.         m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect());
  1046.     }
  1047. }
  1048.  
  1049. void CDIDeviceActionConfigPage::ActionClick(LPDIACTIONW lpac)
  1050. {
  1051.     if (m_pCurControl != NULL)
  1052.     {
  1053.         AssignCurrentControlToAction(lpac);
  1054.  
  1055.         // Set assignment since other views may have the same callout and
  1056.         // they need to be updated too.
  1057.         SetControlAssignments();
  1058.     }
  1059.     // Change the state back to normal
  1060.     ExitAssignState();
  1061. }
  1062.  
  1063. void CDIDeviceActionConfigPage::SetControlAssignments()
  1064. {
  1065.     assert(!IsEqualGUID(m_didi.guidInstance, GUID_NULL));
  1066.  
  1067.     m_pDeviceUI->SetAllControlCaptionsTo(g_tszUnassignedControlCaption);
  1068.  
  1069.     if (m_lpDiac == NULL || m_lpDiac->rgoAction == NULL)
  1070.         return;
  1071.  
  1072.     DWORD i;
  1073.     LPDIACTIONW lpac;
  1074.     for (i = 0, lpac = m_lpDiac->rgoAction; i < m_lpDiac->dwNumActions; i++)
  1075.     {
  1076.         lpac = m_lpDiac->rgoAction + i;
  1077.  
  1078.         if (IsEqualGUID(lpac->guidInstance, GUID_NULL))
  1079.             continue;
  1080.  
  1081.         if (!IsEqualGUID(lpac->guidInstance, m_didi.guidInstance))
  1082.             continue;
  1083.  
  1084.         LPTSTR acname = AllocLPTSTR(lpac->lptszActionName);
  1085.         m_pDeviceUI->SetCaptionForControlsAtOffset(GetOffset(lpac)/*->dwInternalOffset*/, acname, lpac->dwFlags & DIA_APPFIXED);
  1086.         free(acname);
  1087.     }
  1088. }
  1089.  
  1090. void CDIDeviceActionConfigPage::DoViewSel()
  1091. {
  1092.     m_ViewSelWnd.Go(m_hWnd, m_rectIB.left, m_rectIB.top, m_pDeviceUI);
  1093. }
  1094.  
  1095. void CDIDeviceActionConfigPage::OnClick(POINT point, WPARAM, BOOL bLeft)
  1096. {
  1097.     if (!bLeft)
  1098.         return;
  1099.  
  1100.     // Unhighlight current callout
  1101.     ExitAssignState();
  1102.  
  1103.     if (m_pDeviceUI->GetNumViews() > 1)
  1104.     {
  1105.         int iCurView = m_pDeviceUI->GetCurViewIndex();
  1106.  
  1107.         if (PtInRect(&m_rectIBLeft, point))
  1108.             m_pDeviceUI->SetView(iCurView == 0 ? m_pDeviceUI->GetNumViews() - 1 : iCurView - 1);
  1109.         if (PtInRect(&m_rectIBRight, point))
  1110.             m_pDeviceUI->SetView(iCurView == m_pDeviceUI->GetNumViews() - 1 ? 0 : iCurView + 1);
  1111.         if (PtInRect(&m_rectIBText, point))
  1112.             DoViewSel();
  1113.     }
  1114. }
  1115.  
  1116. void CDIDeviceActionConfigPage::OnMouseOver(POINT point, WPARAM fwKeys)
  1117. {
  1118.     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  1119.  
  1120.     // Check view selection area so we can display text in info box.
  1121.     if (m_pDeviceUI->GetNumViews() > 1)
  1122.     {
  1123.         if (PtInRect(&m_rectIB, point))
  1124.         {
  1125.             SetInfoText(IDS_INFOMSG_VIEW_VIEWSEL);
  1126.             return;
  1127.         }
  1128.     }
  1129.  
  1130.     SetAppropriateDefaultText();
  1131. }
  1132.  
  1133. int GetActionIndexFromPointer(LPDIACTIONW p, LPDIACTIONFORMATW paf)
  1134. {
  1135.     if (!p || !paf || !paf->rgoAction)
  1136.         return -1;
  1137.  
  1138.     int index = int((((LPBYTE)p) - ((LPBYTE)paf->rgoAction)) / (DWORD)sizeof(DIACTIONW));
  1139.  
  1140.     assert(&(paf->rgoAction[index]) == p);
  1141.  
  1142.     return index;
  1143. }
  1144.  
  1145. BOOL CDIDeviceActionConfigPage::IsActionAssignedHere(int index)
  1146. {
  1147.     if (!m_lpDiac)
  1148.         return FALSE;
  1149.  
  1150.     if (index < 0 || index >= (int)m_lpDiac->dwNumActions)
  1151.         return FALSE;
  1152.  
  1153.     return IsEqualGUID(m_didi.guidInstance, m_lpDiac->rgoAction[index].guidInstance);
  1154. }
  1155.  
  1156. LRESULT CDIDeviceActionConfigPage::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1157. {
  1158.     switch (msg)
  1159.     {
  1160.         case WM_UNHIGHLIGHT:
  1161.             // Unhighlight current callout
  1162.             ExitAssignState();
  1163.             break;
  1164.  
  1165.         case WM_KEYDOWN:
  1166. #ifdef DBG
  1167.             // In debug version, shift-escape exits the UI.
  1168.             if (wParam == VK_ESCAPE && GetAsyncKeyState(VK_SHIFT) < 0)
  1169.             {
  1170.                 PostMessage(GetParent(m_hWnd), WM_KEYDOWN, wParam, lParam);
  1171.                 break;
  1172.             }
  1173. #endif
  1174.             // If this is a keyboard device, then click-to-pick will take care of the functionalities below.
  1175.             // Process WM_KEYDOWN only for non-keyboard devices.
  1176.             if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD) return 0;
  1177.             switch(wParam)
  1178.             {
  1179.                 case VK_RETURN:
  1180.                     // If we are not in assign state, enter it.
  1181.                     if (m_State == CFGSTATE_NORMAL && m_pCurControl)
  1182.                         EnterAssignState();
  1183.                     break;
  1184.  
  1185.                 case VK_DELETE:
  1186.                     // If we are in assign state and there is a control, unassign it.
  1187.                     if (m_State == CFGSTATE_ASSIGN && m_pCurControl)
  1188.                         UnassignCallout();
  1189.                     break;
  1190.  
  1191.                 case VK_ESCAPE:
  1192.                     if (m_State == CFGSTATE_ASSIGN)
  1193.                         ExitAssignState();
  1194.  
  1195.                     break;
  1196.             }
  1197.             return 0;
  1198.  
  1199.         case WM_FLEXCHECKBOX:
  1200.             switch(wParam)
  1201.             {
  1202.                 case CHKNOTIFY_UNCHECK:
  1203.                         m_pDeviceUI->GetCurView()->SortAssigned(FALSE);
  1204.                         if (m_pCurControl)
  1205.                         {
  1206.                             // Scroll so that we scroll to make this visible since it might be displaced by sorting.
  1207.                             m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect());
  1208.                         }
  1209.                         Invalidate();
  1210.                     break;
  1211.                 case CHKNOTIFY_CHECK:
  1212.                         m_pDeviceUI->GetCurView()->SortAssigned(TRUE);
  1213.                         if (m_pCurControl)
  1214.                         {
  1215.                             // Scroll so that we scroll to make this visible since it might be displaced by sorting.
  1216.                             m_pDeviceUI->GetCurView()->ScrollToMakeControlVisible(m_pCurControl->GetCalloutMaxRect());
  1217.                         }
  1218.                         Invalidate();
  1219.                     break;
  1220.                 case CHKNOTIFY_MOUSEOVER:
  1221.                     SetInfoText(m_CheckBox.GetCheck() ? IDS_INFOMSG_VIEW_SORTENABLED : IDS_INFOMSG_VIEW_SORTDISABLED);
  1222.                     break;
  1223.             }
  1224.             break;
  1225.  
  1226.         case WM_FLEXCOMBOBOX:
  1227.             switch (wParam)
  1228.             {
  1229.                 case FCBN_MOUSEOVER:
  1230.                     if (lParam)
  1231.                     {
  1232.                         CFlexComboBox *pCombo = (CFlexComboBox*)lParam;
  1233.                         if (pCombo->m_hWnd == m_UserNames.m_hWnd)
  1234.                             SetInfoText(m_puig->InEditMode() ? IDS_INFOMSG_EDIT_USERNAME : IDS_INFOMSG_VIEW_USERNAME);
  1235.                         else if (pCombo->m_hWnd == m_Genres.m_hWnd)
  1236.                             SetInfoText(m_puig->InEditMode() ? IDS_INFOMSG_EDIT_GAMEMODE : IDS_INFOMSG_VIEW_GAMEMODE);
  1237.                     }
  1238.                     break;
  1239.  
  1240.                 case FCBN_SELCHANGE:
  1241.                     // Clear the tool tip as the combo-box has closed
  1242.                     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  1243.                     CFlexWnd::s_ToolTip.SetToolTipParent(NULL);
  1244.                     if (m_pUIFrame && m_puig)
  1245.                     {
  1246.                         ExitAssignState();
  1247.                         m_pUIFrame->SetCurGenre(m_Genres.GetSel());
  1248.                         int nUser = m_UserNames.GetSel();
  1249.                         if (m_puig->GetNumUserNames() > 0 && nUser >= m_puig->GetNumUserNames())
  1250.                             nUser = -1;
  1251.                         m_pUIFrame->SetCurUser(m_nPageIndex, nUser);
  1252.                     }
  1253.                     break;
  1254.             }
  1255.             return 0;
  1256.  
  1257.         case WM_FLEXTREENOTIFY:
  1258.         {
  1259.             // Check if this is a mouse over message (just for info box update)
  1260.             if (wParam == FTN_MOUSEOVER)
  1261.             {
  1262.                 SetAppropriateDefaultText();
  1263.                 return FALSE;
  1264.             }
  1265.  
  1266.             if (!lParam)
  1267.                 return FALSE;
  1268.             FLEXTREENOTIFY &n = *((FLEXTREENOTIFY *)(LPVOID)lParam);
  1269.             if (!n.pItem)
  1270.                 return FALSE;
  1271.             switch (wParam)
  1272.             {
  1273.                 case FTN_OWNERDRAW:
  1274.                 {
  1275.                     POINT ofs = {0, 0};
  1276.                     CBitmap *pbmGlyph = NULL;
  1277.                     BOOL bAssigned = FALSE, bAssignedHere = FALSE;
  1278.                     if (n.pItem->IsUserGUID(GUID_ActionItem))
  1279.                     {
  1280.                         LPDIACTIONW lpac = GetItemLpac(n.pItem, 0);
  1281.                         if (lpac)
  1282.                             // We now walk through each DIACTION and find those with action name match, then see if 
  1283.                             // they are assigned anywhere.
  1284.                             for (DWORD i = 0; i < m_lpDiac->dwNumActions; ++i)
  1285.                             {
  1286.                                 if (wcscmp(lpac->lptszActionName, m_lpDiac->rgoAction[i].lptszActionName))
  1287.                                     continue;
  1288.  
  1289.                                 if (bAssignedHere = IsActionAssignedHere(i))
  1290.                                 {
  1291.                                     bAssigned = TRUE;
  1292.                                     break;
  1293.                                 }
  1294.                                 if (m_pUIFrame && m_pUIFrame->QueryActionAssignedAnywhere(m_didi.guidInstance, i) == S_OK)
  1295.                                     bAssigned = TRUE;
  1296.                             }
  1297.                         if (bAssigned || bAssignedHere)
  1298.                         {
  1299.                             pbmGlyph = bAssignedHere ? m_pbmCheckGlyph :
  1300.                                 m_pbmCheckGlyphDark;
  1301.                             pbmGlyph->FigureSize();
  1302.                             ofs.x = 2;
  1303.                             ofs.y = 4;
  1304.                         }
  1305.                     }
  1306.                     else
  1307.                     {
  1308.                         if (n.pItem == m_pRelAxesParent)
  1309.                             pbmGlyph = m_pbmRelAxesGlyph;
  1310.                         if (n.pItem == m_pAbsAxesParent)
  1311.                             pbmGlyph = m_pbmAbsAxesGlyph;
  1312.                         if (n.pItem == m_pButtonParent)
  1313.                             pbmGlyph = m_pbmButtonGlyph;
  1314.                         if (n.pItem == m_pHatParent)
  1315.                             pbmGlyph = m_pbmHatGlyph;
  1316.                         ofs.y = 2;
  1317.                     }
  1318.                     if (!pbmGlyph)
  1319.                         return FALSE;
  1320.                     n.pItem->PaintInto(n.hDC);
  1321.                     RECT rect;
  1322.                     CPaintHelper ph(*m_puig, n.hDC);
  1323.                     ph.SetElement(UIE_GLYPH);
  1324.                     n.pItem->GetMargin(rect);
  1325.                     pbmGlyph->Draw(n.hDC, ofs.x, rect.top + ofs.y);
  1326.                     return TRUE;
  1327.                 }
  1328.  
  1329.                 case FTN_CLICK:
  1330.                     // We cannot assign a different control to this action if it has the DIA_APPFIXED flag.
  1331.                     if (n.pItem->IsUserGUID(GUID_ActionItem) && GetItemLpac(n.pItem) && !(GetItemLpac(n.pItem)->dwFlags & DIA_APPFIXED))
  1332.                     {
  1333.                         m_Tree.SetCurSel(n.pItem);
  1334.                         ActionClick(GetItemLpac(n.pItem));
  1335.                     }
  1336.                     else
  1337.                     {
  1338. #ifdef CFGUI__ALLOW_USER_ACTION_TREE_BRANCH_MANIPULATION
  1339.                         if (!n.pItem->IsExpanded())
  1340.                             n.pItem->Expand();
  1341.                         else
  1342.                             n.pItem->Collapse();
  1343. #endif
  1344.                     }
  1345.                     break;
  1346.             }
  1347.             break;
  1348.         }
  1349.     }
  1350.  
  1351.     return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  1352. }
  1353.  
  1354. void CDIDeviceActionConfigPage::SetInvalid(LPDIACTIONW lpac)
  1355. {
  1356.     lpac->guidInstance = GUID_NULL;
  1357.     lpac->dwObjID = (DWORD)-1;
  1358. }
  1359.  
  1360. DWORD CDIDeviceActionConfigPage::GetOffset(LPDIACTIONW lpac)
  1361. {
  1362.     return lpac ? lpac->dwObjID : (DWORD)-1;
  1363. }
  1364.  
  1365. void CDIDeviceActionConfigPage::SetOffset(LPDIACTIONW lpac, DWORD ofs)
  1366. {
  1367.     assert(lpac != NULL);
  1368.     if (!lpac)
  1369.         return;
  1370.     lpac->dwObjID = ofs;
  1371. }
  1372.  
  1373. HRESULT CDIDeviceActionConfigPage::InitLookup()
  1374. {
  1375.     DIDEVOBJSTRUCT os;
  1376.  
  1377.     HRESULT hresult = FillDIDeviceObjectStruct(os, m_lpDID);
  1378.  
  1379.     if (FAILED(hresult))
  1380.         return hresult;
  1381.  
  1382.     for (int i = 0; i < os.nObjects; i++)
  1383.     {
  1384.         DIDEVICEOBJECTINSTANCEW &doi = os.pdoi[i];
  1385.         offset_objid.add(doi.dwOfs, doi.dwType);
  1386.     }
  1387.  
  1388.     return S_OK;
  1389. }
  1390.  
  1391. HRESULT CDIDeviceActionConfigPage::SetEditLayout(BOOL bEditLayout)
  1392. {
  1393.     m_pDeviceUI->SetEditMode(bEditLayout);
  1394.     return S_OK;
  1395. }
  1396.  
  1397.  
  1398. BOOL CDIDeviceActionConfigPage::IsControlMapped(CDeviceControl *pControl)
  1399. {
  1400.     if (pControl == NULL)
  1401.         return FALSE;
  1402.  
  1403.     if (!pControl->IsOffsetAssigned())
  1404.         return FALSE;
  1405.  
  1406.     if (m_lpDiac == NULL)
  1407.         return FALSE;
  1408.     
  1409.     for (DWORD i = 0; i < m_lpDiac->dwNumActions; i++)
  1410.         if (GetOffset(&(m_lpDiac->rgoAction[i])) == pControl->GetOffset())
  1411.             return TRUE;
  1412.  
  1413.     return FALSE;
  1414. }
  1415.  
  1416. void CDIDeviceActionConfigPage::InitDevice()
  1417. {
  1418.     if (m_lpDID == NULL || m_pDeviceUI == NULL || m_pUIFrame == NULL)
  1419.         return;
  1420.  
  1421.     HWND hWndMain = m_pUIFrame->GetMainHWND();
  1422.     if (!hWndMain)
  1423.         return;
  1424.  
  1425.     // don't do anything if this is a mouse
  1426.     switch ((DWORD)(LOBYTE(LOWORD(m_pDeviceUI->m_didi.dwDevType))))
  1427.     {
  1428.         case DI8DEVTYPE_MOUSE:
  1429.             return;
  1430.     }
  1431.  
  1432.     // init/prepare...
  1433.     int i;
  1434.     const DIDEVOBJSTRUCT &os = m_pDeviceUI->m_os;
  1435.     int nObjects = os.nObjects;
  1436.  
  1437.     DIDATAFORMAT df;
  1438.     df.dwSize = sizeof(DIDATAFORMAT);
  1439.     df.dwObjSize = sizeof(DIOBJECTDATAFORMAT);
  1440.     df.dwFlags = DIDF_ABSAXIS;
  1441.     df.dwDataSize = sizeof(DWORD) * (DWORD)nObjects;
  1442.     df.dwNumObjs = (DWORD)nObjects;
  1443.     df.rgodf = (DIOBJECTDATAFORMAT *)malloc(sizeof(DIOBJECTDATAFORMAT) * nObjects);
  1444.     if (df.rgodf == NULL)
  1445.     {
  1446.         etrace1(_T("Could not allocate DIOBJECTDATAFORMAT array of %d elements\n"), nObjects);
  1447.         return;
  1448.     }
  1449.  
  1450.     m_cbDeviceDataSize = df.dwDataSize;
  1451.     for (int c = 0; c < 2; c++)
  1452.     {
  1453.         if (m_pDeviceData[c] != NULL)
  1454.             free(m_pDeviceData[c]);
  1455.         m_pDeviceData[c] = (DWORD *)malloc(m_cbDeviceDataSize);
  1456.         if (m_pDeviceData[c] == NULL)
  1457.             etrace2(_T("Could not allocate device data buffer %d of %d bytes\n"), c, m_cbDeviceDataSize);
  1458.     }
  1459.     m_nOnDeviceData = 0;
  1460.     m_bFirstDeviceData = TRUE;
  1461.  
  1462.     for (i = 0; i < nObjects; i++)
  1463.     {
  1464.         DIOBJECTDATAFORMAT *podf = &(df.rgodf[i]);
  1465.         podf->pguid = NULL;
  1466.         podf->dwOfs = i * sizeof(DWORD);
  1467.         podf->dwType = os.pdoi[i].dwType;
  1468.         podf->dwFlags = 0;
  1469.     }
  1470.  
  1471.     if (df.rgodf != NULL)
  1472.     {
  1473.         HRESULT hr = m_lpDID->SetDataFormat(&df);
  1474.         free(df.rgodf);
  1475.         df.rgodf = NULL;
  1476.  
  1477.         if (FAILED(hr))
  1478.         {
  1479.             etrace1(_T("SetDataFormat() failed, returning 0x%08x\n"), hr);
  1480.         }
  1481.         else
  1482.         {
  1483.             hr = m_lpDID->SetCooperativeLevel(hWndMain, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
  1484.             if (FAILED(hr))
  1485.                 etrace1(_T("SetCooperativeLevel() failed, returning 0x%08x\n"), hr);
  1486.  
  1487.             DIPROPRANGE range;
  1488.             range.diph.dwSize = sizeof(DIPROPRANGE);
  1489.             range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1490.             range.diph.dwObj = 0;
  1491.             range.diph.dwHow = DIPH_DEVICE;
  1492.             range.lMin = DEVICE_POLLING_AXIS_MIN;
  1493.             range.lMax = DEVICE_POLLING_AXIS_MAX;
  1494.  
  1495.             hr = m_lpDID->SetProperty(DIPROP_RANGE, (LPCDIPROPHEADER)&range);
  1496.             if (FAILED(hr))
  1497.                 etrace1(_T("SetProperty(DIPROP_RANGE, ...) failed, returning 0x%08x\n"), hr);
  1498.  
  1499.             hr = m_lpDID->Acquire();
  1500.             if (FAILED(hr))
  1501.                 etrace1(_T("Acquire() failed, returning 0x%08x\n"), hr);
  1502.         }
  1503.     }
  1504. }
  1505.  
  1506. void CALLBACK CDIDeviceActionConfigPage::DeviceTimerProc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
  1507. {
  1508.     if (!IsWindow((HWND)dwUser)) return;  // Verify that dwUser is a valid window handle
  1509.     CDIDeviceActionConfigPage *pPage = (CDIDeviceActionConfigPage *)GetFlexWnd((HWND)dwUser);  // Get flex object
  1510.     if (pPage)
  1511.         pPage->DeviceTimer();
  1512. }
  1513.  
  1514. void CDIDeviceActionConfigPage::DeviceTimer()
  1515. {
  1516.     DWORD *pOldData = m_pDeviceData[m_nOnDeviceData];
  1517.     m_nOnDeviceData = (m_nOnDeviceData + 1) & 1;
  1518.     DWORD *pData = m_pDeviceData[m_nOnDeviceData];
  1519.  
  1520.     if (m_lpDID == NULL || pData == NULL || pOldData == NULL)
  1521.     {
  1522.         // Required data not available.  Return and there'll be no more timer callbacks.
  1523.         etrace(_T("DeviceTimer() failed\n"));
  1524.         return;
  1525.     }
  1526.  
  1527.     // Get device data only if this page is visible.
  1528.     if (m_lpDiac)
  1529.     {
  1530.         HRESULT hr = m_lpDID->Poll();
  1531.         if (SUCCEEDED(hr))
  1532.         {
  1533.             hr = m_lpDID->GetDeviceState(m_cbDeviceDataSize, pData);
  1534.             if (SUCCEEDED(hr))
  1535.             {
  1536.                 if (!m_bFirstDeviceData)
  1537.                 {
  1538.                     DeviceDelta(pData, pOldData);
  1539.                 } else
  1540.                 {
  1541.                     m_bFirstDeviceData = FALSE;
  1542.                 }
  1543.             } else
  1544.             {
  1545.                 etrace1(_T("GetDeviceState() failed, returning 0x%08x\n"), hr);
  1546.             }
  1547.         } else
  1548.         {
  1549.             etrace1(_T("Poll() failed, returning 0x%08x\n"), hr);
  1550.         }
  1551.     }
  1552.  
  1553.     // Set the next timer event.
  1554.     if (g_fptimeSetEvent)
  1555.         g_fptimeSetEvent(DEVICE_POLLING_INTERVAL, DEVICE_POLLING_INTERVAL,
  1556.                          CDIDeviceActionConfigPage::DeviceTimerProc, (DWORD_PTR)m_hWnd, TIME_ONESHOT);
  1557. }
  1558.  
  1559. void CDIDeviceActionConfigPage::DeviceDelta(DWORD *pData, DWORD *pOldData)
  1560. {
  1561.     if (pData == NULL || pOldData == NULL || m_pDeviceUI == NULL)
  1562.         return;
  1563.  
  1564.     const DIDEVOBJSTRUCT &os = m_pDeviceUI->m_os;
  1565.  
  1566.     // see which objects changed
  1567.     for (int i = 0; i < os.nObjects; i++)
  1568.     {
  1569.         // for axes, we need to do special processing
  1570.         if (os.pdoi[i].dwType & DIDFT_AXIS)
  1571.         {
  1572.             BOOL bSig = FALSE, bOldSig = FALSE;
  1573.  
  1574.             StoreAxisDeltaAndCalcSignificance(os.pdoi[i],
  1575.                 pData[i], pOldData[i], bSig, bOldSig);
  1576.  
  1577.             AxisDelta(os.pdoi[i], bSig, bOldSig);
  1578.  
  1579.             continue;
  1580.         }
  1581.  
  1582.         // for all others, skip that which didn't change
  1583.         if (pData[i] == pOldData[i])
  1584.             continue;
  1585.  
  1586.         // pass to appropriate delta function
  1587.         DWORD dwObjId = os.pdoi[i].dwType;
  1588.         if (dwObjId & DIDFT_BUTTON)
  1589.             ButtonDelta(os.pdoi[i], pData[i], pOldData[i]);
  1590.         else if (dwObjId & DIDFT_POV)
  1591.             PovDelta(os.pdoi[i], pData[i], pOldData[i]);
  1592.     }
  1593. }
  1594.  
  1595. void CDIDeviceActionConfigPage::StoreAxisDeltaAndCalcSignificance(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD olddata, BOOL &bSig, BOOL &bOldSig)
  1596. {
  1597.     // see if this object has an axis value array
  1598.     int i;
  1599.     if (objid_avai.getright(doi.dwType, i))
  1600.     {
  1601.         AxisValueArray &ar = m_AxisValueArray[i];
  1602.         int on = ar[0] + 1;
  1603.         if (on >= ar.GetSize())
  1604.             on = DEVICE_POLLING_ACBUF_START_INDEX;
  1605.         ar[0] = on;
  1606.         int delta = abs(int(data) - int(olddata));
  1607.         // Scale up the delta if this is a wheel axis as wheels are harder to move generally.
  1608.         if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_DRIVING && doi.guidType == GUID_XAxis)
  1609.             delta = delta * DEVICE_POLLING_WHEEL_SCALE_FACTOR;
  1610.         if (delta < DEVICE_POLLING_AXIS_MINDELTA)
  1611.             delta = 0;
  1612.         int cumul = ar[1];  // Retrieve cumulative value for easier processing
  1613.         cumul -= ar[on];  // Subtract value in current slot from cumul since it's being thrown away.
  1614.         cumul += delta;  // Add current delta to cumul
  1615.         ar[on] = delta;  // Store the delta at current slot
  1616.         ar[1] = cumul;  // Save cumulative value
  1617.  
  1618.         bOldSig = (BOOL)ar[2];
  1619.         ar[2] = int(bSig = cumul > DEVICE_POLLING_AXIS_SIGNIFICANT);
  1620.         if (bSig)
  1621.         {
  1622.             // This axis is about to be activated.  We now reset the history and cumulative movement since we don't need them any more.
  1623.             ar[0] = DEVICE_POLLING_ACBUF_START_INDEX;
  1624.             ar[1] = 0;
  1625.             ar[2] = FALSE;
  1626.             for (int c = DEVICE_POLLING_ACBUF_START_INDEX;
  1627.                     c < DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION; c++)
  1628.                 ar[c] = 0;
  1629.         }
  1630.     }
  1631.     else
  1632.     {
  1633.         i = m_AxisValueArray.GetSize();
  1634.         m_AxisValueArray.SetSize(i + 1);
  1635.         objid_avai.add(doi.dwType, i);
  1636.         AxisValueArray &ar = m_AxisValueArray[i];
  1637.         ar.SetSize(DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION);
  1638.         ar[0] = DEVICE_POLLING_ACBUF_START_INDEX;
  1639.         ar[1] = 0;
  1640.         ar[2] = FALSE;
  1641.         for (int c = DEVICE_POLLING_ACBUF_START_INDEX;
  1642.                 c < DEVICE_POLLING_ACBUF_START_INDEX + DEVICE_POLLING_AXIS_ACCUMULATION; c++)
  1643.             ar[c] = 0;
  1644.         
  1645.         bOldSig = bSig = FALSE;
  1646.     }
  1647. }
  1648.  
  1649. void CDIDeviceActionConfigPage::AxisDelta(const DIDEVICEOBJECTINSTANCEW &doi, BOOL data, BOOL old)
  1650. {
  1651.     if (data && !old)
  1652.     {
  1653.         if (m_State == CFGSTATE_NORMAL)
  1654.             ActivateObject(doi);
  1655.     }
  1656.     if (old && !data)
  1657.         DeactivateObject(doi);
  1658. }
  1659.  
  1660. void CDIDeviceActionConfigPage::ButtonDelta(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD old)
  1661. {
  1662.     static DWORD dwLastOfs;
  1663.     static DWORD dwLastTimeStamp;
  1664.  
  1665.     if (data && !old)
  1666.     {
  1667.         // Do special processing for keyboard
  1668.         if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD)
  1669.         {
  1670.             // If this is an ENTER key, we enter the assign state if not already in it.
  1671.             if (doi.dwOfs == DIK_RETURN || doi.dwOfs == DIK_NUMPADENTER)
  1672.             {
  1673.                 if (m_State == CFGSTATE_NORMAL && m_pCurControl)
  1674.                     EnterAssignState();
  1675.                 return;  // Do nothing other than entering the assign state.  No highlighting
  1676.             }
  1677.  
  1678.             // DELETE key case
  1679.             // If we are in assign state and there is a control, unassign it.
  1680.             if (doi.dwOfs == DIK_DELETE && m_State == CFGSTATE_ASSIGN && m_pCurControl)
  1681.                 {
  1682.                     UnassignCallout();
  1683.                     return;  // Don't highlight or do pick to click for delete if this press happens during assign state.
  1684.                 }
  1685.  
  1686.             // ESCAPE key case
  1687.             if (doi.dwOfs == DIK_ESCAPE && m_State == CFGSTATE_ASSIGN)
  1688.             {
  1689.                 ExitAssignState();
  1690.                 return;
  1691.             }
  1692.  
  1693.             // For all other keys, still process click-to-pick or highlighting.
  1694.         }
  1695.  
  1696.         // Enter assign state if this is a double activation
  1697.         if (m_State == CFGSTATE_NORMAL)
  1698.         {
  1699.             ActivateObject(doi);
  1700.  
  1701.             if (doi.dwOfs == dwLastOfs && dwLastTimeStamp + GetDoubleClickTime() > GetTickCount())
  1702.             {
  1703.                 // We check if a callout for this control exists.  If not, do not enter assign state.
  1704.                 CDeviceView *pCurView = m_pDeviceUI->GetCurView();
  1705.                 CDeviceControl *pControl = pCurView->GetControlFromOfs(doi.dwType);
  1706.                 if (pControl)
  1707.                     EnterAssignState();
  1708.             }
  1709.             dwLastOfs = doi.dwOfs;
  1710.             dwLastTimeStamp = GetTickCount();
  1711.         }
  1712.     }
  1713.     if (old && !data)
  1714.         DeactivateObject(doi);
  1715. }
  1716.  
  1717. void CDIDeviceActionConfigPage::PovDelta(const DIDEVICEOBJECTINSTANCEW &doi, DWORD data, DWORD old)
  1718. {
  1719.     BOOL d = data != -1, o = old != -1;
  1720.  
  1721.     if (d && !o)
  1722.     {
  1723.         if (m_State == CFGSTATE_NORMAL)
  1724.             ActivateObject(doi);
  1725.     }
  1726.     if (o && !d)
  1727.         DeactivateObject(doi);    
  1728. }
  1729.  
  1730. void CDIDeviceActionConfigPage::ActivateObject(const DIDEVICEOBJECTINSTANCEW &doi)
  1731. {
  1732.     if (m_pDeviceUI == NULL)
  1733.         return;
  1734.  
  1735.  
  1736.     CDeviceView *pCurView = m_pDeviceUI->GetCurView(), *pView = pCurView;
  1737.     if (pView == NULL)
  1738.         return;
  1739.  
  1740.     CDeviceControl *pControl = pView->GetControlFromOfs(doi.dwType);
  1741.     if (pControl == NULL)
  1742.     {
  1743.         for (int i = 0; i < m_pDeviceUI->GetNumViews(); i++)
  1744.         {
  1745.             pView = m_pDeviceUI->GetView(i);
  1746.             if (pView == NULL)
  1747.                 continue;
  1748.  
  1749.             pControl = pView->GetControlFromOfs(doi.dwType);
  1750.             if (pControl != NULL)
  1751.                 break;
  1752.         }
  1753.  
  1754.         if (pControl != NULL && pView != NULL && pView != pCurView)
  1755.         {
  1756.             // switch to view
  1757.             m_pDeviceUI->SetView(pView);
  1758.             SetControlAssignments();
  1759.             SetCurrentControl(NULL);
  1760.         }
  1761.     }
  1762.  
  1763.     if (pControl != NULL)
  1764.         SetCurrentControl(pControl);
  1765.  
  1766.     SetAppropriateDefaultText();
  1767. }
  1768.  
  1769. void CDIDeviceActionConfigPage::DeactivateObject(const DIDEVICEOBJECTINSTANCEW &doi)
  1770. {
  1771.     // Add code that needs to be run when deactivating here.
  1772. }
  1773.  
  1774. HRESULT CDIDeviceActionConfigPage::Unacquire()
  1775. {
  1776.     if (m_lpDID != NULL)
  1777.         m_lpDID->Unacquire();
  1778.  
  1779.     return S_OK;
  1780. }
  1781.  
  1782. HRESULT CDIDeviceActionConfigPage::Reacquire()
  1783. {
  1784.     InitDevice();
  1785.  
  1786.     return S_OK;
  1787. }
  1788.  
  1789. void CDIDeviceActionConfigPage::EnterAssignState()
  1790. {
  1791.     if (!m_puig->InEditMode())
  1792.         return;
  1793.     if (!m_pCurControl || m_pCurControl->IsFixed())
  1794.         return;
  1795.     SetInfoText(IDS_INFOMSG_EDIT_EDITMODEENABLED);
  1796.     m_State = CFGSTATE_ASSIGN;  // Into the assign state.
  1797.     ShowCurrentControlAssignment();  // Show the tree
  1798.     m_Tree.Invalidate();
  1799.     Invalidate();
  1800. }
  1801.  
  1802. void CDIDeviceActionConfigPage::ExitAssignState()
  1803. {
  1804.     m_State = CFGSTATE_NORMAL;  // Out of the assign state.
  1805.     SetCurrentControl(NULL);  // Unselect the control
  1806.     ShowCurrentControlAssignment();  // Show the tree
  1807.     m_Tree.Invalidate();
  1808.     Invalidate();
  1809.     SetAppropriateDefaultText();
  1810. }
  1811.  
  1812. HRESULT CDIDeviceActionConfigPage::SetInfoText(int iCode)
  1813. {
  1814.     // We check for special code -1 here.  This is only called by CConfigWnd, and means that we should
  1815.     // call SetAppropriateDefaultText to display proper text.
  1816.     if (iCode == -1)
  1817.         SetAppropriateDefaultText();
  1818.     else
  1819.         m_InfoBox.SetText(iCode);
  1820.     return S_OK;
  1821. }
  1822.  
  1823. void CDIDeviceActionConfigPage::SetAppropriateDefaultText()
  1824. {
  1825.     if (m_puig->InEditMode())
  1826.     {
  1827.         if (m_State == CFGSTATE_ASSIGN)
  1828.             SetInfoText(IDS_INFOMSG_EDIT_EDITMODEENABLED);
  1829.         else if (m_pCurControl)
  1830.         {
  1831.             if (m_pCurControl->IsFixed())
  1832.                 SetInfoText(IDS_INFOMSG_APPFIXEDSELECT);
  1833.             else
  1834.                 SetInfoText(IDS_INFOMSG_EDIT_CTRLSELECTED);
  1835.         }
  1836.         else
  1837.         {
  1838.             if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_KEYBOARD)
  1839.                 SetInfoText(IDS_INFOMSG_EDIT_KEYBOARD);
  1840.             else
  1841.             if (LOBYTE(m_didi.dwDevType) == DI8DEVTYPE_MOUSE)
  1842.                 SetInfoText(IDS_INFOMSG_EDIT_MOUSE);
  1843.             else
  1844.                 SetInfoText(IDS_INFOMSG_EDIT_DEVICE);
  1845.         }
  1846.     } else
  1847.         SetInfoText(IDS_INFOMSG_VIEW_DEVICE);
  1848. }
  1849.